﻿using System;
using System.Configuration;
using System.Linq;
using Common.Logging;
using Newtonsoft.Json;
using OutDbContext;
using Senparc.Weixin.Entities;
using Senparc.Weixin.MP.AdvancedAPIs;
using Senparc.Weixin.MP.Helpers;
using Senparc.Weixin.MP.TenPayLib;

namespace Out.Service.Helpers
{
    public class TenpayV2Helper
    {
        private static readonly ILog Logger = LogManager.GetLogger(typeof(TenpayV2Helper));

        private static TenPayInfo _tenPayInfo;

        public static TenPayInfo TenPayInfo
        {
            get
            {
                if (_tenPayInfo == null)
                {
                    _tenPayInfo =
                        TenPayInfoCollection.Data[ConfigurationManager.AppSettings["WeixinPay_PartnerId"]];
                }
                return _tenPayInfo;
            }
        }

        /// <summary>
        /// 生成公众号网页支付的请求包
        /// </summary>
        /// <param name="tradeName">支付单名称，128 字节以下，如：广东妇保就诊挂号，广东妇保就诊缴费</param>
        /// <param name="tradeNo">商户系统内部的订单号， 32个字符内，可包含字母，确保在商户系统唯一</param>
        /// <param name="totalFee">订单总金额， 单位为分</param>
        /// <param name="clientIp">指用户浏览器端IP，不是商户服务器，格式为 IPV4</param>
        /// <param name="startTime">订单生成时间，格式为 yyyyMMddHHmmss 如2009年 12 月 25 日 9点 10 分 10 秒表示为 20091225091010，时区为 GMT+8 beijing；该时间取自商户服务器；</param>
        /// <param name="expireTime">订单失效时间，格式为 yyyyMMddHHmmss 如2009年 12 月 25 日 9点 10 分 10 秒表示为 20091225091010，时区为 GMT+8 beijing；该时间取自商户服务器；</param>
        /// <returns></returns>
        public static string GenerateWxPayParameter(string tradeName, string tradeNo, string totalFee, string clientIp, string startTime, string expireTime)
        {
            //创建支付应答对象
            RequestHandler packageReqHandler = new RequestHandlerFix();
            //初始化
            packageReqHandler.Init();
            packageReqHandler.SetKey(TenPayInfo.Key);

            //设置package订单参数
            packageReqHandler.SetParameter("partner", TenPayInfo.PartnerId);          //商户号
            packageReqHandler.SetParameter("fee_type", "1");                    //币种，1人民币
            packageReqHandler.SetParameter("input_charset", "UTF-8");
            packageReqHandler.SetParameter("bank_type", "WX");
            packageReqHandler.SetParameter("out_trade_no", tradeNo);      //商家订单号
            packageReqHandler.SetParameter("total_fee", totalFee);                   //商品金额,以分为单位(money * 100).ToString()
            packageReqHandler.SetParameter("notify_url", TenPayInfo.TenPayNotify);          //接收财付通通知的URL
            packageReqHandler.SetParameter("body", tradeName);                        //商品描述
            packageReqHandler.SetParameter("spbill_create_ip", clientIp);   //用户的公网ip，不是商户服务器IP
            packageReqHandler.SetParameter("time_start", startTime); //支付开始时间
            packageReqHandler.SetParameter("time_expire", expireTime);//支付结束时间

            //获取package包
            var packageValue = packageReqHandler.GetRequestURL();

            //调起微信支付签名
            var timeStamp = TenPayUtil.GetTimestamp();
            var nonceStr = TenPayUtil.GetNoncestr();

            //设置支付参数
            RequestHandler paySignReqHandler = new RequestHandler(null);
            paySignReqHandler.SetParameter("appid", TenPayInfo.AppId);
            paySignReqHandler.SetParameter("appkey", TenPayInfo.AppKey);
            paySignReqHandler.SetParameter("noncestr", nonceStr);
            paySignReqHandler.SetParameter("timestamp", timeStamp);
            paySignReqHandler.SetParameter("package", packageValue);
            var paySign = paySignReqHandler.CreateSHA1Sign();

            var payRequestParam = JsonConvert.SerializeObject(new
            {
                appId = TenPayInfo.AppId,
                timeStamp = timeStamp,
                nonceStr = nonceStr,
                package = packageValue,
                signType = "SHA1",
                paySign = paySign
            });

            return payRequestParam;
        }

        /// <summary>
        /// 发送发货通知
        /// </summary>
        /// <param name="openId">用户的 OpenId</param>
        /// <param name="transId">交易单号</param>
        /// <param name="outTradeNo">第三方订单号 （交易号和第三方订单号只需填写一个）</param>
        /// <param name="deliverStatus">发货状态，1 表明成功，0 表明失败，失败时需要在 deliver_msg 填上失败原因</param>
        /// <param name="deliverMsg">发货状态信息，失败时可以填上 UTF8 编码的错误提示信息，比如“该商品已退款”</param>
        /// <returns></returns>
        public static DeliverNotifyResult DoDeliverNotify(string openId, string transId, string outTradeNo,
            string deliverStatus, string deliverMsg)
        {
            //调起微信支付签名
            var timeStamp = TenPayUtil.GetTimestamp();

            //设置支付参数
            RequestHandler paySignReqHandler = new RequestHandlerFix();
            paySignReqHandler.SetParameter("appid", TenPayInfo.AppId);
            paySignReqHandler.SetParameter("openId", openId);
            paySignReqHandler.SetParameter("transId", transId);
            paySignReqHandler.SetParameter("deliver_TimesTamp", timeStamp);
            paySignReqHandler.SetParameter("out_Trade_No", outTradeNo);
            paySignReqHandler.SetParameter("deliver_Status", deliverStatus);
            paySignReqHandler.SetParameter("deliver_Msg", deliverMsg);
            var appSignature = paySignReqHandler.CreateSHA1Sign();

            WxJsonResult result;

            try
            {
                result = TenPay.Delivernotify(TenPayInfo.AppId, openId, transId, outTradeNo,
                                 timeStamp, deliverStatus, deliverMsg, appSignature, "sha1");
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("发送微信发送通知时出错，信息：{0}", JsonConvert.SerializeObject(ex));
                throw;
            }

            return new DeliverNotifyResult
            {
                ErrorCode = result.errcode.ToInt32OrDefault(),
                ErrorMsg = result.errmsg
            };
        }

        /// <summary>
        /// 执行微信退款操作
        /// </summary>
        /// <param name="openId">被退款人OpenId</param>
        /// <param name="outTradeNo">
        /// out_trade_no
        /// 商户订单号
        /// 商户系统内部的订单号, out_trade_no 和
        /// transaction_id 至少一个必填，同时存在时
        /// transaction_id 优先
        /// </param>
        /// <param name="transactionId">
        /// transaction_id
        /// 财付通订单号
        /// 财付通交易号, out_trade_no 和
        /// transaction_id 至少一个必填，同时存在时
        /// transaction_id 优先
        /// </param>
        /// <param name="outRefundNo">
        /// out_refund_no
        /// 商户退款单号
        /// 商户退款单号，32 个字符内、可包含字母,确
        /// 保在商户系统唯一。同个退款单号多次请求
        /// 财付通当一个单处理，只会退一次款。如果出
        /// 现退款不成功，请采用原退款单号重新发起，
        /// 避免出现重复退款。
        /// </param>
        /// <param name="totalFee">
        /// total_fee
        /// 总金额
        /// 订单总金额，单位为分
        /// </param>
        /// <param name="refundFee">
        /// refund_fee
        /// 退款金额
        /// 退款总金额,单位为分,可以做部分退款
        /// </param>
        /// <returns></returns>
        public static RefundResult DoRefund(string openId, string outTradeNo, string transactionId, string outRefundNo,
            int totalFee, int refundFee)
        {
            //创建请求对象
            RefundRequestHandler reqHandler = new RefundRequestHandlerFix();

            //通信对象
            TenPayHttpClient httpClient = new TenPayHttpClient();

            //应答对象
            ClientResponseHandler resHandler = new ClientResponseHandler();

            //-----------------------------
            //设置请求参数
            //-----------------------------
            reqHandler.Init();
            reqHandler.SetKey(TenPayInfo.Key);

            reqHandler.SetParameter("partner", TenPayInfo.PartnerId);
            //out_trade_no和transaction_id至少一个必填，同时存在时transaction_id优先
            reqHandler.SetParameter("out_trade_no", outTradeNo);
            reqHandler.SetParameter("transaction_id", transactionId);
            reqHandler.SetParameter("out_refund_no", outRefundNo);
            reqHandler.SetParameter("total_fee", totalFee.ToString());
            reqHandler.SetParameter("refund_fee", refundFee.ToString());
            reqHandler.SetParameter("op_user_id", TenPayInfo.PartnerId);
            reqHandler.SetParameter("op_user_passwd", MD5UtilHelper.GetMD5("111111", "GBK"));
            reqHandler.SetParameter("service_version", "1.1");

            string requestUrl = reqHandler.GetRequestURL();
            httpClient.SetCertInfo(@"PartnerCertFile\1219701001_20151230120522.pfx", "801493");
            //设置请求内容
            httpClient.SetReqContent(requestUrl);
            //设置超时
            httpClient.SetTimeOut(30);

            string rescontent = "";
            //后台调用
            if (httpClient.Call())
            {
                //获取结果
                rescontent = httpClient.GetResContent();

                resHandler.SetKey(TenPayInfo.Key);
                //设置结果参数
                resHandler.SetContent(rescontent);

                //创建退款记录日志
                using (var ctx = new GdsfybjyWeiXinEntities())
                {
                    var model = new RefundLog();
                    model.OrderNo = outTradeNo;
                    model.OrderFee = totalFee;
                    model.RefundNo = outRefundNo;
                    model.WxTransactionId = transactionId;
                    model.CreateTime = DateTime.Now;
                    model.RefundStatus = 0;
                    ctx.RefundLogs.Add(model);
                    ctx.SaveChanges();
                }

                //判断签名及结果
                if (resHandler.IsTenpaySign() && resHandler.GetParameter("retcode") == "0")
                {
                    var result = new RefundResult();
                    result.RetCode = resHandler.GetParameter("retcode").ToInt32OrDefault();
                    result.RetMsg = resHandler.GetParameter("retmsg");
                    result.Partner = resHandler.GetParameter("partner");
                    result.TransactionId = resHandler.GetParameter("transaction_id");
                    result.OutTradeNo = resHandler.GetParameter("out_trade_no");
                    result.OutRefundNo = resHandler.GetParameter("out_refund_no");
                    result.RefundId = resHandler.GetParameter("refund_id");
                    result.RefundChannel = resHandler.GetParameter("refund_channel").ToInt32OrDefault();
                    result.RefundFee = resHandler.GetParameter("refund_fee").ToInt32OrDefault();
                    result.RefundStatus = resHandler.GetParameter("refund_status").ToInt32OrDefault();
                    result.RecvUserId = resHandler.GetParameter("recv_user_id");
                    result.ReccvUserName = resHandler.GetParameter("reccv_user_name");

                    //更新退款记录
                    using (var ctx = new GdsfybjyWeiXinEntities())
                    {
                        var model = ctx.RefundLogs.First(x => x.RefundNo == outRefundNo);
                        model.WxRefundId = resHandler.GetParameter("refund_id");
                        model.RefundFee = resHandler.GetParameter("refund_fee").ToInt32OrDefault();
                        model.RefundChannel = resHandler.GetParameter("refund_channel").ToString();
                        model.RefundReason = resHandler.GetParameter("retmsg");
                        model.RecieveUserId = resHandler.GetParameter("recv_user_id");
                        model.RecieveUserName = resHandler.GetParameter("reccv_user_name");
                        model.RefundStatus = resHandler.GetParameter("refund_status").ToInt32OrDefault();
                        ctx.SaveChanges();                        
                    }

                    // 用户退款结果通知再执行退款的RMQ中发起
                    return result;
                }
                else
                {
                    //错误时，返回结果未签名。
                    //如包格式错误或未确认结果的，请使用原来订单号重新发起，确认结果，避免多次操作
                    throw new Exception("返回结果未签名");
                }

            }
            else
            {
                //后台调用通信失败
                //有可能因为网络原因，请求已经处理，但未收到应答。
                throw new Exception("调用通信失败");
            }
        }
    }

    public class DeliverNotifyResult
    {
        /// <summary>
        /// errcode
        /// 返回状态码
        /// 返回状态码，0 表示成功，其他未定义
        /// </summary>
        public int ErrorCode { get; set; }

        /// <summary>
        /// errmsg
        /// 返回信息
        /// 返回信息，如非空，为错误原因
        /// </summary>
        public string ErrorMsg { get; set; }
    }

    public class RefundResult
    {
        /// <summary>
        /// retcode
        /// 返回状态码
        /// 返回状态码，0 表示成功，其他未定义
        /// </summary>
        public int RetCode { get; set; }

        /// <summary>
        /// retmsg
        /// 返回信息
        /// 返回信息，如非空，为错误原因
        /// </summary>
        public string RetMsg { get; set; }

        /// <summary>
        /// partner
        /// 商户号
        /// 商户号
        /// </summary>
        public string Partner { get; set; }

        /// <summary>
        /// transaction_id
        /// 财付通订单号
        /// 财付通交易号，28 位长的数值，其中前10 位为商户号，之后8 位为订单产生的日期，如20090415，最后10 位是流水号。
        /// </summary>
        public string TransactionId { get; set; }

        /// <summary>
        /// out_trade_no
        /// 商户订单号
        /// 商户系统内部的订单号
        /// </summary>
        public string OutTradeNo { get; set; }

        /// <summary>
        /// out_refund_no
        /// 商户退款单号
        /// 商户退款单号
        /// </summary>
        public string OutRefundNo { get; set; }

        /// <summary>
        /// refund_id
        /// 财付通退款单号
        /// 财付通退款单号
        /// </summary>
        public string RefundId { get; set; }

        /// <summary>
        /// refund_channel
        /// 退款渠道
        /// 退款渠道,0:退到财付通、1:退到银行
        /// </summary>
        public int RefundChannel { get; set; }

        /// <summary>
        /// refund_fee
        /// 退款金额
        /// 退款总金额,单位为分,可以做部分退款
        /// </summary>
        public int RefundFee { get; set; }

        /// <summary>
        /// refund_status
        /// 退款状态
        /// 退款状态：
        /// 4，10：退款成功。
        /// 3，5，6：退款失败。
        /// 8，9，11：退款处理中。
        /// 1，2：未确定，需要商户原退款单号重新发起。
        /// 7：转入代发，退款到银行发现用户的卡作废或
        ///    者冻结了，导致原路退款银行卡失败，资金回
        ///    流到商户的现金帐号，需要商户人工干预，通
        ///    过线下或者财付通转账的方式进行退款
        /// </summary>
        public int RefundStatus { get; set; }

        /// <summary>
        /// recv_user_id
        /// 接收人帐号
        /// 转账退款接收退款的财付通帐号
        /// </summary>
        public string RecvUserId { get; set; }

        /// <summary>
        /// reccv_user_name
        /// 转账退款接收退款的姓名(需与接收退款的财
        /// 付通帐号绑定的姓名一致)
        /// </summary>
        public string ReccvUserName { get; set; }
    }

    public class RequestHandlerFix : RequestHandler
    {
        public RequestHandlerFix() : base(null)
        { }

        protected override string GetCharset()
        {
            return "UTF-8";
        }
    }

    public class RefundRequestHandlerFix : RefundRequestHandler
    {
        public RefundRequestHandlerFix() : base(null)
        { }

        protected override string GetCharset()
        {
            return "UTF-8";
        }

        public new void DoSend()
        {

        }
    }
}